package com.guojiang.guoaicodemother.core;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.guojiang.guoaicodemother.ai.AiCodeGeneratorService;
import com.guojiang.guoaicodemother.ai.AiCodeGeneratorServiceFactory;
import com.guojiang.guoaicodemother.ai.model.HtmlCodeResult;
import com.guojiang.guoaicodemother.ai.model.MultiFileCodeResult;
import com.guojiang.guoaicodemother.ai.model.message.AiResponseMessage;
import com.guojiang.guoaicodemother.ai.model.message.ToolExecutedMessage;
import com.guojiang.guoaicodemother.ai.model.message.ToolRequestMessage;
import com.guojiang.guoaicodemother.core.parser.CodeParserExecutor;
import com.guojiang.guoaicodemother.core.saver.CodeFileSaverExecutor;
import com.guojiang.guoaicodemother.exception.BusinessException;
import com.guojiang.guoaicodemother.exception.ErrorCode;
import com.guojiang.guoaicodemother.exception.ThrowUtils;
import com.guojiang.guoaicodemother.model.enums.CodeGenTypeEnum;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.service.TokenStream;
import dev.langchain4j.service.tool.ToolExecution;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;

/**
 * 代码生成门面类，组合代码生成和保存功能
 */
@Service
@Slf4j
public class AiCodeGeneratorFacade {

    @Resource
    private AiCodeGeneratorServiceFactory aiCodeGeneratorServiceFactory;

    /**
     * 统一入口：根据类型生成并保存代码文件
     *
     * @param userMessage     用户提示词
     * @param codeGenTypeEnum 代码生成类型
     * @return
     */
    public File generateAndSaveCode(String userMessage, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
        ThrowUtils.throwIf(ObjectUtil.isNull(codeGenTypeEnum), ErrorCode.PARAMS_ERROR, "生成类型参数异常");
        // 根据 AppId 获取相应的 AI 服务实例
        AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getAiCodeGeneratorService(appId, codeGenTypeEnum);
        return switch (codeGenTypeEnum) {
            case HTML -> {
                HtmlCodeResult htmlCodeResult = aiCodeGeneratorService.generateHTMLCode(userMessage);
                yield CodeFileSaverExecutor.executeCodeSaver(htmlCodeResult, CodeGenTypeEnum.HTML, appId);
            }
            case MULTI_FILE -> {
                MultiFileCodeResult multiFileCodeResult = aiCodeGeneratorService.generateMultiFileCode(userMessage);
                yield CodeFileSaverExecutor.executeCodeSaver(multiFileCodeResult, CodeGenTypeEnum.MULTI_FILE, appId);
            }
            default -> {
                String errorMessage = "不支持的生成类型：" + codeGenTypeEnum.getValue();
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
            }
        };
    }

    /**
     * 统一入口：根据类型生成并保存代码文件（流式）
     *
     * @param userMessage     用户提示词
     * @param codeGenTypeEnum 代码生成类型
     * @param appId           应用 ID
     * @return
     */
    public Flux<String> generateAndSaveCodeStreaming(String userMessage, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
        ThrowUtils.throwIf(ObjUtil.isNull(codeGenTypeEnum), ErrorCode.PARAMS_ERROR, "生成类型参数异常");
        AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getAiCodeGeneratorService(appId, codeGenTypeEnum);
        return switch (codeGenTypeEnum) {
            case HTML -> {
                Flux<String> result = aiCodeGeneratorService.generateHTMLCodeStreaming(userMessage);
                yield processCodeStreaming(result, CodeGenTypeEnum.HTML, appId);
            }
            case MULTI_FILE -> {
                Flux<String> result = aiCodeGeneratorService.generateMultiFileCodeStreaming(userMessage);
                yield processCodeStreaming(result, CodeGenTypeEnum.MULTI_FILE, appId);
            }
            case VUE_PROJECT -> {
                TokenStream tokenStream = aiCodeGeneratorService.generateVueProjectCodeStreaming(appId, userMessage);
                yield processTokenStream(tokenStream);
            }
            default -> {
                String errorMessage = "不支持的生成类型：" + codeGenTypeEnum.getValue();
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
            }
        };
    }

    /**
     * 将 TokenStream 转换为 Flux<String>，并传递工具调用信息
     *
     * @param tokenStream TokenStream 对象
     * @return Flux<String> 流式响应
     */
    private Flux<String> processTokenStream(TokenStream tokenStream) {
        return Flux.create(sink -> {
            //在转换过程中，将 TokenStream 的不同事件传递给不同的消息对象，并转换为 JSON 字符串
            tokenStream.onPartialResponse((String partialResponse) -> {
                        AiResponseMessage aiResponseMessage = new AiResponseMessage(partialResponse);
                        sink.next(JSONUtil.toJsonStr(aiResponseMessage));
                    })
                    .onPartialToolExecutionRequest((index, toolExecutionRequest) -> {
                        ToolRequestMessage toolRequestMessage = new ToolRequestMessage(toolExecutionRequest);
                        sink.next(JSONUtil.toJsonStr(toolRequestMessage));
                    })
                    .onToolExecuted((ToolExecution toolExecution) -> {
                        ToolExecutedMessage toolExecutedMessage = new ToolExecutedMessage(toolExecution);
                        sink.next(JSONUtil.toJsonStr(toolExecutedMessage));
                    })
                    .onCompleteResponse((ChatResponse response) -> {
                        sink.complete();
                    })
                    .onError((Throwable error) -> {
                        error.printStackTrace();
                        sink.error(error);
                    })
                    .start();
        });
    }

    /**
     * 生成并保存多文件代码（流式）
     *
     * @param codeStream
     * @param codeGenTypeEnum
     * @param appId
     * @return
     */
    private Flux<String> processCodeStreaming(Flux<String> codeStream, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
        // 运用 StringBuilder，定义字符串拼接器，当流式返回所有代码之后，再保存代码文件
        StringBuilder codeBuilder = new StringBuilder();
        return codeStream.doOnNext(codeBuilder::append) // 实时收集代码片段
                .doOnComplete(() -> {
                    try {
                        // 收集完代码后，根据参数传入的代码生成类型，解析成 HtmlCodeResult 或 MultiFileCodeResult 对象
                        String completeCode = codeBuilder.toString();
                        // 调用代码解析执行器的静态解析方法，获取解析后的结果
                        Object codeParsedResult = CodeParserExecutor.executeCodeParser(completeCode, codeGenTypeEnum);
                        // 调用代码文件保存执行器静态保存方法，输出日志，文件绝对路径
                        File file = CodeFileSaverExecutor.executeCodeSaver(codeParsedResult, codeGenTypeEnum, appId);
                        log.info("代码文件已成功保存于：{}", file.getAbsolutePath());
                    } catch (Exception e) {
                        log.error("代码文件保存失败，", e.getMessage());
                    }
                });
    }
}
